﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using RRRSRoguelike.Entities;
using RRRSRoguelikeData;
using RRRSRoguelike.Enums;
using RRRSRoguelike.Managers;

namespace RRRSRoguelike.Helpers
{
    public class DungeonLevel
    {
    	
    	public LevelData Data;
        public IList<Tile> walls;

        public List<Tile[,]> layers = new List<Tile[,]>();
              
        public Tile OpenTile;       
        public int level;

        public DungeonLevel(int level,int xMax, int yMax)
        {
            this.level = level;
            walls = new List<Tile>();

            for (int i = 0; i < Enum.GetNames(typeof(Layers)).Count(); i++)
                layers.Add(new Tile[xMax, yMax]);
            
            OpenTile = new Tile();
            
            BuildRandomDungeon();
        }

        public override string ToString()
        {
            return "Dungeon Level: " + level;
        }

        private void InitializeTiles()
        {
            for (int i = 0; i < Constants.DungeonHeight; i++)
            {
                for (int j = 0; j < Constants.DungeonWidth; j++)
                {
                	Tile t = layers[(int)Layers.Base][j, i];
                	
                	Dungeon d = DungeonManager.Instance.Dungeon;   	
                	
                    layers[(int)Layers.Base][j, i] = 
                    	new Tile(new Point(i, j),DungeonManager.Instance.Dungeon.Data.Tiles["Floor"]);
                }
            } 
        }

        public static Point GetNewPosition(Point position, Direction direction, int distance)
        {
            Point newPoint = position;
            switch (direction)
            {
                case Direction.N:
                    newPoint.Y -= distance;
                    break;
                case Direction.S:
                    newPoint.Y += distance;
                    break;
                case Direction.E:
                    newPoint.X += distance;
                    break;
                case Direction.W:
                    newPoint.X -= distance;
                    break;
            }
            return newPoint;
        }
        
        private void BuildRandomDungeon()
        {
            InitializeTiles();
            for (int i = 0; i < Constants.DungeonWidth; i++)
            {
            	Tile top = new Tile(new Point(i, 0), 
            	                    DungeonManager.Instance.Dungeon.Data.Tiles["Wall"]);
                walls.Add(top);
                Tile bottom = new Tile(new Point(i, Constants.DungeonHeight - 1),
                                       DungeonManager.Instance.Dungeon.Data.Tiles["Wall"]);
                walls.Add(bottom);
            }

            for (int i = 0; i < Constants.DungeonHeight; i++)
            {
                Tile left = new Tile(new Point(0, i),
            	                     DungeonManager.Instance.Dungeon.Data.Tiles["Wall"]);
                walls.Add(left);
                Tile right = new Tile(new Point(Constants.DungeonWidth - 1, i),
                                      DungeonManager.Instance.Dungeon.Data.Tiles["Wall"]);
                walls.Add(right);
            }
        }

        public bool IsOKToMove(Point p)
        {
        	//not ok
        	bool b = false;
        	if (IsInBounds(p))
        	{
        		//ok if in bounds
        		b = true;
        		// not ok if any layers tile is unwalkable. (allows for unwalkable effects. illusion spells anyone?)
        		List<Object> l = GetTiles(p);
        		foreach (Object o in l) 
        		{
        			Tile t = (Tile)o;
        			if (!t.Walkable) b = false;
        		}
        	
        	
        	}
        	return b;
        	
        }
        
 		public bool SquareContainsTrap(Point p)
        {
        	//not ok
        	bool b = false;
        	if (IsInBounds(p))
        	{
        		//ok if in bounds
        		// not ok if any layers tile is unwalkable. (allows for unwalkable effects. illusion spells anyone?)
        		List<Object> l = GetTiles(p);
        		if (l.OfType<Trap>().Count() > 0) b = true;
        	
        	
        	}
        	return b;
        	
        }       
        

        public bool IsInBounds(Point p)
        { 
        	return
        		p.X >= 0 && p.X < Constants.DungeonWidth &&
        		p.Y >= 0 && p.Y < Constants.DungeonHeight;
        }

        public static Point ClampToDungeon(Point p, bool insideWalls = false)
        {
        	if (insideWalls)
        	{
        		p.X = Math.Max(Math.Min(p.X, Constants.DungeonWidth-2),1);
        		p.Y = Math.Max(Math.Min(p.Y, Constants.DungeonHeight-2),1);	
        	}
        	else
        	{
            //includes walls
            p.X = Math.Max(Math.Min(p.X, Constants.DungeonWidth-1),0);
            p.Y = Math.Max(Math.Min(p.Y, Constants.DungeonHeight-1),0);
        	}
            return p;
        }

        internal void AddTile(Tile t, Layers layer)
        {
            layers[(int)layer][t.X, t.Y] = t;
        }

        internal Object GetTile(Point point,Layers layer)
        {
            return layers[(int)layer][point.X, point.Y];
        }

        internal Object GetTopTile(Point point) 
        {
            Tile t;

            for (int i = Enum.GetNames(typeof(Layers)).Count()-1; i >= 0; i--)
            {
                if (layers[i][point.X, point.Y] != null)
                {
                    t = layers[i][point.X, point.Y];
                    return t;
                }
            }

            return null;
        }
        
        internal List<Object> GetTiles(Point point)
        {
        	List<Object> l = new List<object>();
        	
        	
            for (int i = Enum.GetNames(typeof(Layers)).Count()-1; i >= 0; i--)
            {
                if (layers[i][point.X, point.Y] != null)
                {
                	l.Add(layers[i][point.X, point.Y]);
                }
            }
            
            return l;
        }

        internal void Clear()
        {
            for (int i = 0; i < Constants.DungeonHeight; i++)
            {
                for (int j = 0; j < Constants.DungeonWidth; j++)
                {
                    for (int l = 1; l < Enum.GetNames(typeof(Layers)).Count(); l++)
                    layers[l][j, i] = null;
                }
            } 
            InitializeTiles();
        }

        internal void AddStairwell(Stairwell stairwell, Layers layer)
        {
            layers[(int)layer][stairwell.X, stairwell.Y] = stairwell;
        }

        public void CreateRandomDungeonObjects()
        {
            DungeonManager.Instance.CreatureManager.ClearMonsters();
            DungeonManager.Instance.CreatureManager.AddRandomMonstersByLevel();

            DungeonManager.Instance.PropManager.ClearProps();
            DungeonManager.Instance.PropManager.AddProps();
        }

        public Point GetValidRandomPoint(bool callIsOkToPlaceMonster)
        {
            Point p;
            do
            {
                p = new Point(DungeonManager.Instance.DungeonBuilderRandom.Next(Constants.DungeonWidth), DungeonManager.Instance.DungeonBuilderRandom.Next(Constants.DungeonWidth));
            }
            while (!IsOKToMove(p));
            
            if(callIsOkToPlaceMonster)
            	if(!IsOKToPlaceMonster(p))
            		p = GetValidRandomPoint(true);
            
            return p;
        }
	
	            public bool IsOKToPlaceMonster(Point p)
	 	        {
	                //this might have a problem if an effect is hiding something.
	                object t = GetTopTile(p);
	
	                System.Type type = t.GetType() ;
	
	                Tile tl = (Tile)t;
	                
	                if (t is Stairwell | t is Player | t is Monster | !tl.Walkable)
	 	                return false;
	 	 
	 	            return true;
	 	            
	 	        } 

    }
}
